Logo FSharp.Analyzers.SDK

Unit testing an analyzer

To help analyzer authors testing their analyzers, there's a dedicated testing package. It contains easy to use utility functions to create a context for the analyzer and assertion helpers.

FSharp.Analyzers.SDK.Testing.mkOptionsFromProject creates the FSharpProjectOptions for the given framework (e.g. net7.0) and the given list of packages to reference. FSharp.Analyzers.SDK.Testing.getContext then takes the FSharpProjectOptions and the source code to test and creates a CliContext you can pass along to your analyzer function. The module FSharp.Analyzers.SDK.Testing.Assert offers various functions to help with assertion statements from your favorite unit testing framework. For a complete example of an unit testing project, take a look at OptionAnalyzer.Test in the samples folder of the SDK repository.

open FSharp.Compiler.CodeAnalysis
open FSharp.Analyzers.SDK.Testing
open OptionAnalyzer
open NUnit.Framework

let mutable projectOptions: FSharpProjectOptions = FSharpProjectOptions.zero

[<SetUp>]
let Setup () =
    task {
        let! opts =
            mkOptionsFromProject
                "net10.0"
                [
                    // The SDK uses this in a "dotnet add package x --version y" command
                    // to generate the needed FSharpProjectOptions
                    {
                        Name = "Newtonsoft.Json"
                        Version = "13.0.3"
                    }
                ]

        projectOptions <- opts
    }

[<Test>]
let ``warnings are emitted`` () =
    async {
        let source =
            """
module M

let notUsed() =
    let option : Option<int> = None
    option.Value
    """

        let ctx = getContext projectOptions source
        let! msgs = optionValueAnalyzer ctx
        Assert.IsNotEmpty msgs
        Assert.IsTrue(Assert.messageContains "Option.Value" msgs[0])
    }

Previous Next

Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Compiler
namespace FSharp.Compiler.CodeAnalysis
namespace FSharp.Analyzers
namespace FSharp.Analyzers.SDK
module Testing
from FSharp.Analyzers.SDK
module OptionAnalyzer
namespace NUnit
namespace NUnit.Framework
val mutable projectOptions: FSharpProjectOptions
type FSharpProjectOptions =
  {
    ProjectFileName: string
    ProjectId: string option
    SourceFiles: string array
    OtherOptions: string array
    ReferencedProjects: FSharpReferencedProject array
    IsIncompleteTypeCheckEnvironment: bool
    UseScriptResolutionRules: bool
    LoadTime: DateTime
    UnresolvedReferences: FSharpUnresolvedReferencesSet option
    OriginalLoadReferences: (range * string * string) list
    ...
  }
property FSharpProjectOptions.zero: FSharpProjectOptions with get
Multiple items
type SetUpAttribute =
  inherit NUnitAttribute
  new: unit -> unit

--------------------
SetUpAttribute() : SetUpAttribute
val Setup: unit -> System.Threading.Tasks.Task<unit>
val task: TaskBuilder
val opts: FSharpProjectOptions
Creates a classlib project in a temporary folder to gather the needed FSharpProjectOptions.

framework: The target framework for the tested code to use. E.g. net6.0, net7.0
additionalPkgs: A list of additional packages that should be referenced. The tested code can use these.
returns: FSharpProjectOptions

val mkOptionsFromProject:
    framework: string ->
    additionalPkgs: Package list ->
        System.Threading.Tasks.Task<FSharpProjectOptions>
Multiple items
type TestAttribute =
  inherit NUnitAttribute
  interface ISimpleTestBuilder
  interface IApplyToTest
  interface IImplyFixture
  new: unit -> unit
  member ApplyToTest: test: Test -> unit
  member BuildFrom: method: IMethodInfo * suite: Test -> TestMethod
  member Author: string
  member Description: string
  member ExpectedResult: obj
  ...

--------------------
TestAttribute() : TestAttribute
val async: AsyncBuilder
val source: string
val ctx: FSharp.Analyzers.SDK.CliContext
Creates CliContext for a given source and options.

opts: The project options to use.
source: The file to analyze.
returns: CliContext

val getContext:
    opts: FSharpProjectOptions ->
    source: string ->
        FSharp.Analyzers.SDK.CliContext
val msgs: FSharp.Analyzers.SDK.Message list
val optionValueAnalyzer:
    ctx: FSharp.Analyzers.SDK.CliContext ->
        Async<FSharp.Analyzers.SDK.Message list>
type Assert =
  static member AreEqual: expected: float * actual: float * delta: float * message: string * args: obj array -> unit + 5 overloads
  static member AreNotEqual: expected: obj * actual: obj * message: string * args: obj array -> unit + 1 overload
  static member AreNotSame: expected: obj * actual: obj * message: string * args: obj array -> unit + 1 overload
  static member AreSame: expected: obj * actual: obj * message: string * args: obj array -> unit + 1 overload
  static member ByVal: actual: obj * expression: IResolveConstraint -> unit + 1 overload
  static member Catch: code: TestDelegate * message: string * args: obj array -> exn + 5 overloads
  static member CatchAsync: code: AsyncTestDelegate * message: string * args: obj array -> exn + 5 overloads
  static member Contains: expected: obj * actual: ICollection * message: string * args: obj array -> unit + 1 overload
  static member DoesNotThrow: code: TestDelegate * message: string * args: obj array -> unit + 1 overload
  static member DoesNotThrowAsync: code: AsyncTestDelegate * message: string * args: obj array -> unit + 1 overload
  ...
Assert.IsNotEmpty(collection: System.Collections.IEnumerable) : unit
Assert.IsNotEmpty(aString: string) : unit
Assert.IsNotEmpty(collection: System.Collections.IEnumerable, message: string, args: obj array) : unit
Assert.IsNotEmpty(aString: string, message: string, args: obj array) : unit
Assert.IsTrue(condition: bool) : unit
Assert.IsTrue(condition: System.Nullable<bool>) : unit
Assert.IsTrue(condition: bool, message: string, args: obj array) : unit
Assert.IsTrue(condition: System.Nullable<bool>, message: string, args: obj array) : unit
val messageContains:
    expectedContent: string ->
    msg: FSharp.Analyzers.SDK.Message ->
        bool

Type something to start searching.